package com.commons.cachad;

import net.rubyeye.xmemcached.MemcachedClient;
import net.rubyeye.xmemcached.exception.MemcachedException;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.Assert;

import java.net.InetSocketAddress;
import java.util.Collection;
import java.util.Map;
import java.util.concurrent.TimeoutException;

/**
 * 缓存
 * Created by FCHEN on 2016/1/24.
 */
public class Memcached {
    private static Logger LOG = LoggerFactory.getLogger(Memcached.class);
    private MemcachedClient memcachedClient;

    private static Long DEFAULT_OP_TIMEOUT = 10000L;//10min

    //@Value("${cache.default.expire.time}")
    //public static int defaultExpireTime = 2592000;//一个月缓存
    public static int DEFAULT_EXPIRE_TIME = 86400;//默认缓存1天：24*60*60=86400

    /**
     * Memcached是否可用
     *
     * @return
     * @author FCHEN 2016-01-24
     */
    @Deprecated
    public boolean isAvaliable() {
        if (memcachedClient.isShutdown()) {
            LOG.error("memcached客户端已关闭");
            return false;
        }
        Map<InetSocketAddress, String> map = null;
        try {
            map = memcachedClient.getVersions();
        } catch (Exception e) {
            LOG.error("获取memcached server version时异常", e);
        }
        if (map == null || map.size() == 0) {
            LOG.error("当前没有可用的memcached server");
            return false;
        }
        //cache正常可用
        return true;
    }

    /**
     * @param key
     * @return
     * @author FCHEN 2016-01-24
     */
    public Object getValue(String key) {
        Object value = null;
        Assert.notNull(key);
        try {
            value = memcachedClient.get(key);
        } catch (TimeoutException e) {
            LOG.error("Cache TimeoutException", e);
        } catch (InterruptedException e) {
            LOG.error("Cache InterruptedException", e);
        } catch (MemcachedException e) {
            LOG.error("Cache MemcachedException", e);
        }
        return value;
    }

    /**
     * @param key
     * @param t
     * @return
     * @author FCHEN 2016-01-24
     */
    public <T> T getValue(String key, Class<T> t) {
        T value = null;
        Assert.notNull(key);
        try {
            value = this.memcachedClient.get(key);
        } catch (TimeoutException e) {
            LOG.error("Cache TimeoutException", e);
        } catch (InterruptedException e) {
            LOG.error("Cache InterruptedException", e);
        } catch (MemcachedException e) {
            LOG.error("Cache MemcachedException", e);
        }
        return value;
    }

    /**
     * 在cache中保存value
     *
     * @param key
     * @param value
     * @author FCHEN 2016-01-24
     */
    public void setValue(String key, Object value) {
        Assert.notNull(key, "cache key is null.");
        try {
            memcachedClient.set(key, DEFAULT_EXPIRE_TIME, value);
        } catch (TimeoutException e) {
            LOG.error("Cache TimeoutException", e);
        } catch (InterruptedException e) {
            LOG.error("Cache InterruptedException", e);
        } catch (MemcachedException e) {
            LOG.error("Cache MemcachedException", e);
        }
    }

    /**
     * 在cache中保存value
     *
     * @param key
     * @param value
     * @param exp   表示被保存的时长，单位:秒
     * @author FCHEN 2016-01-24
     */
    public void setValue(String key, int exp, Object value) {
        Assert.notNull(key, "cache key is null.");
        Assert.notNull(exp > 0, "exp must greate than zero.");
        try {
            memcachedClient.set(key, exp, value);
        } catch (TimeoutException e) {
            LOG.error("Cache TimeoutException", e);
        } catch (InterruptedException e) {
            LOG.error("Cache InterruptedException", e);
        } catch (MemcachedException e) {
            LOG.error("Cache MemcachedException", e);
        }
    }

    /**
     * 删除cache保存的value
     *
     * @param key
     * @return
     * @author FCHEN 2016-01-24
     */
    public Boolean remove(String key) {
        try {
            return memcachedClient.delete(key, DEFAULT_OP_TIMEOUT);
        } catch (TimeoutException e) {
            LOG.error("Cache TimeoutException", e);
        } catch (InterruptedException e) {
            LOG.error("Cache InterruptedException", e);
        } catch (MemcachedException e) {
            LOG.error("Cache MemcachedException", e);
        }
        return Boolean.FALSE;
    }


    /**
     * 删除cache保存的value,设置超时时间
     *
     * @param key
     * @param opTimeout
     * @return
     * @author FCHEN 2016-01-24
     */
    public Boolean remove(String key, Long opTimeout) {
        try {
            return memcachedClient.delete(key, opTimeout);
        } catch (TimeoutException e) {
            LOG.error("Cache TimeoutException", e);
        } catch (InterruptedException e) {
            LOG.error("Cache InterruptedException", e);
        } catch (MemcachedException e) {
            LOG.error("Cache MemcachedException", e);
        }
        return Boolean.FALSE;
    }

    /**
     * Get.
     */
    public <T> T get(String key) {
        try {
            return (T) memcachedClient.get(key);
        } catch (Exception e) {
            handleException(e, key);
            return null;
        }
    }

    /**
     * GetBulk.
     */
    public Map getBulk(Collection keys) {
        try {
            return memcachedClient.get(keys);
        } catch (Exception e) {
            handleException(e, StringUtils.join(keys, ","));
            return null;
        }
    }

    /**
     * Set.
     */
    public void asyncSet(String key, int expiredTime, Object value) {
        try {
            memcachedClient.setWithNoReply(key, expiredTime, value);
        } catch (Exception e) {
            throw handleException(e, key);
        }
    }

    /**
     * Set.
     */
    public boolean set(String key, int expiredTime, Object value) {
        try {
            return memcachedClient.set(key, expiredTime, value);
        } catch (Exception e) {
            throw handleException(e, key);
        }
    }

    /**
     * Delete.
     */
    public boolean delete(String key) {
        try {
            return memcachedClient.delete(key);
        } catch (Exception e) {
            throw handleException(e, key);
        }
    }

    /**
     * Incr.
     */
    public long incr(String key, int by, long defaultValue) {
        try {
            return memcachedClient.incr(key, by, defaultValue);
        } catch (Exception e) {
            throw handleException(e, key);
        }
    }

    /**
     * Decr.
     */
    public long decr(String key, int by, long defaultValue) {
        try {
            return memcachedClient.decr(key, by, defaultValue);
        } catch (Exception e) {
            throw handleException(e, key);
        }
    }

    private RuntimeException handleException(Exception e, String key) {
        LOG.error("xmemcached client exception with key:" + key, e);
        return new RuntimeException(e);
    }

    public MemcachedClient getMemcachedClient() {
        return memcachedClient;
    }

    @Autowired(required = false)
    public void setMemcachedClient(MemcachedClient memcachedClient) {
        this.memcachedClient = memcachedClient;
        this.memcachedClient.setOpTimeout(5000L);
        this.memcachedClient.setConnectTimeout(10000);
        this.memcachedClient.setEnableHeartBeat(false);
    }
}